/** \file Debug.cc
 * Definitions for Debug class.
 *
 * (c) Copyright Semih Cemiloglu
 * All rights reserved, see COPYRIGHT file for details.
 *
 * $Id: Debug.cc,v 1.3 2008/06/19 06:48:08 semihc Exp $
 *
 *
 */

#include "Debug.hh"
#include <iostream>
#include <iomanip>
#include <time.h>
#ifdef TC_PLAT_Win32
# include <sys/timeb.h>
#endif
#ifdef TC_PLAT_Linux
# include <sys/time.h>
#endif


using namespace std;
using namespace TC;


// STATIC MEMBERS
TC_VOLATILE int Debug::s_Level = Debug::e_Debug;
TC_VOLATILE bool Debug::s_PreciseTiming = true;
TC_VOLATILE bool Debug::s_PreciseLocation = true;
FILE* Debug::s_Sink = stdout;
  
Debug::Debug(int level, const char* file, int line)
    : m_level(level), m_file(file), m_line(line)
{
  // Preallocate string buffer to avoid multiple
  // memory allocations
  string s;
  s.reserve(1024);
  m_os.str(s);
  //TODO:if(isWritable())
  addPrefix();
}


Debug::~Debug()
{
  //TODO if(isWritable())

  if(s_Sink)
    fputs(m_os.str().c_str(), s_Sink);
}                  

#if 0
void Debug::addPrefixOld()
{
  char fmt[] = "%y%m%d %T";
  char bfr[64];
  int tid = 0;
  char lc = GetLevelCode(m_level);
  time_t now = time(0);
  
  cftime(bfr, fmt, &now);
  m_os << bfr;
  
  if(s_PreciseTiming) {
    struct timeval tv;
    gettimeofday(&tv, 0);
    m_os << '.' << setw(3) << setfill('0') 
         << static_cast<long>(tv.tv_usec) / 1000L;
  }
  
  m_os << '-' << lc << tid << '-';
}
#endif


void Debug::addPrefix()
{
  int tid = 0;
  char lc = GetLevelCode(m_level);
  
  addTimestamp();
  m_os << '-' << lc << tid << '-';
  if(s_PreciseTiming)
    m_os << m_file << ':' << m_line << '-';
}    


void Debug::addTimestamp()
{
  const int BufSize = 64;
  char buf[BufSize];
    
#if defined(TC_PLAT_Win32)
  const char fmt[] = "%y%m%d %H:%M:%S";
  struct __timeb64 now;
  struct tm t;
  errno_t err;

  // Get current time
  _ftime64_s(&now);
  // Convert it to local time
  err = _localtime64_s(&t, &now.time); 
  // Convert it to string
  strftime(buf, BufSize, fmt, &t);
  m_os << buf;
  if(s_PreciseTiming) {
    m_os << '.' << setw(3) << setfill('0') << now.millitm ;
  }
  
#elif defined(TC_PLAT_Sun)
  char fmt[] = "%y%m%d %T";
  struct timeval tv;
  gettimeofday(&tv, 0);
  time_t now = static_cast<time_t>(tv.tv_sec); //time(0);
  cftime(buf, fmt, &now);
  m_os << buf;
  if(s_PreciseTiming) {
    m_os << '.' << setw(3) << setfill('0') 
         << static_cast<long>(tv.tv_usec) / 1000L;
  }
  
#else /* Linux */
  char fmt[] = "%y%m%d %T";
  struct timeval tv;
  gettimeofday(&tv, 0);
  time_t now = static_cast<time_t>(tv.tv_sec); //time(0);
  struct tm t;
  localtime_r(&now, &t);
  strftime(buf, BufSize, fmt, &t);
  m_os << buf;
  if(s_PreciseTiming) {
    m_os << '.' << setw(3) << setfill('0') 
         << static_cast<long>(tv.tv_usec) / 1000L;
  }
  
#endif
}
      

FILE* Debug::GetSinkFile()
{
  return s_Sink;
}


void Debug::SetSinkFile(FILE* fs)
{
  if(s_Sink)
    fflush(s_Sink);
  s_Sink = fs;
}


char Debug::GetLevelCode(int level)
{
  char lc = '?';
  
  switch(level) {
    case e_Fatal:
      lc = 'F'; break;
    case e_Error:
      lc = 'E'; break;
    case e_Warn:
      lc = 'W'; break;
    case e_Info:
      lc = 'I'; break;
  };

  if(level >= e_Debug)
    lc = 'D';
    
  return lc;
}
